/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "msdp_common_algorithm.h"

#include <algorithm>
#include <thread>
#include "spatial_awareness_common.h"
#include "constants.h"

namespace OHOS {
namespace Msdp {
bool MsdpCommonAlgorithm::Enable(std::shared_ptr<SpatialCallback> &callback)
{
    SPATIAL_LOGI(MODULE_SERVICE, "%{public}s Init enable", __func__);

    if (rdbMgr_ == nullptr) {
        rdbMgr_ = std::make_unique<SpatialRdbManager>();
    }

    SpatialRdbManager::SpatialEventCallback eventCallback =
        std::bind(&MsdpCommonAlgorithm::ReceiveCommonEventData, this, std::placeholders::_1);
    rdbMgr_->RegisterEventCallback(SpatialRdbManager::CMD_COMMON, eventCallback);

    callback_ = callback;

    bool ret = rdbMgr_->Init(SpatialRdbManager::CMD_COMMON);
    if (!ret) {
        SPATIAL_LOGE(MODULE_ALGORITHM, "rdb manager init fail");
        return ret;
    }
    return true;
}

bool MsdpCommonAlgorithm::Disable()
{
    return true;
}

bool MsdpCommonAlgorithm::ReceiveCommonEventData(const SpatialEvent &event)
{
    SPATIAL_LOGI(MODULE_ALGORITHM, "%{public}s enter", __func__);
    updateEvent_.direction = event.direction;
    updateEvent_.distance = event.distance;
    updateEvent_.angle = event.angle;
    SPATIAL_LOGI(MODULE_SERVICE,
        "%{public}s OnResult direction: %{public}d, distance: %{public}f, angle: %{public}f"
        "exist: %{public}d, innerouter: %{public}d", __func__, updateEvent_.direction,
        updateEvent_.distance, updateEvent_.angle, updateEvent_.exist, updateEvent_.innerOuter);
    return true;
}

bool MsdpCommonAlgorithm::Trigger(SpatialDevice center, std::vector<SpatialDevice> around)
{
    SPATIAL_LOGI(MODULE_ALGORITHM, "%{public}s enter", __func__);
    if (around.empty()) {
        SPATIAL_LOGE(MODULE_ALGORITHM, "%{public}s peer device list is empty", __func__);
        return false;
    }

    std::set<std::string, MsdpCommonAlgorithm::compare> deviceIdSet;
    deviceIdSet.insert(center.deviceId);
    for (auto &iter : around) {
        deviceIdSet.insert(iter.deviceId);
    }

    if (center.deviceId != *deviceIdSet.begin()) {
        SPATIAL_LOGI(MODULE_ALGORITHM, "%{public}s peer device list is empty", __func__);
        std::thread(&MsdpCommonAlgorithm::CallbackEnd, this).detach();
        return true;
    }

    std::thread(&MsdpCommonAlgorithm::CallbackTrigger, this, center, around).detach();
    return true;
}

void MsdpCommonAlgorithm::CallbackEnd()
{
    callback_->OnEnd();
}


void MsdpCommonAlgorithm::CallbackTrigger(SpatialDevice center, std::vector<SpatialDevice> around)
{
    SPATIAL_LOGI(MODULE_ALGORITHM, "%{public}s enter", __func__);
    for (auto &iter : around) {
        if (center.deviceId == iter.deviceId) {
            continue;
        }
        SpatialEvent event;
        event.direction = updateEvent_.direction;
        event.distance = updateEvent_.distance;
        event.angle = updateEvent_.angle;
        event.device.deviceId = iter.deviceId;
        event.device.deviceName = iter.deviceName;
        event.device.typeId = iter.typeId;
        SPATIAL_LOGI(MODULE_ALGORITHM, "%{public}s callback event", __func__);
        callback_->OnResult(event);
    }
    callback_->OnEnd();
}

MsdpAlgorithmCapability MsdpCommonAlgorithm::GetCapability()
{
    MsdpAlgorithmCapability capability;
    capability.type = AlgorithmType::COMMON;
    capability.version = "1.0.0";
    capability.vecdor = "com.vector";
    capability.description = "CommonAlgorithm";
    capability.mediumMap_.insert(std::make_pair(TransmissionMedium::WIFI, MediumPriority::HIGH));
    capability.mediumMap_.insert(std::make_pair(TransmissionMedium::ETHERNET, MediumPriority::MIDDLE));
    return capability;
}

extern "C" MsdpAlgorithmInterface *Create(void)
{
    SPATIAL_LOGI(MODULE_ALGORITHM, "%{public}s enter", __func__);
    return new MsdpCommonAlgorithm();
}

extern "C" void Destroy(MsdpAlgorithmInterface *algorithm)
{
    SPATIAL_LOGI(MODULE_ALGORITHM, "%{public}s enter", __func__);
    delete algorithm;
}
} // namespace Msdp
} // namespace OHOS